package org.black.phoneUtility.content.contentProvider;

import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;

import org.black.phoneUtility.database.sqlite.PhoneUtilitySQLiteOpenHelper;
import org.black.phoneUtility.database.sqlite.PhoneUtilityTransaction;
import org.black.phoneUtility.provider.PhoneUtilityContract;

import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteTransactionListener;
import android.net.Uri;
import android.text.TextUtils;

public class PhoneUtilityContentProvider extends ContentProvider implements
		SQLiteTransactionListener {
	static final String PHONE_UTILITY_CONTACT_TAG = "phont_utility_contact_tag";

	private ThreadLocal<PhoneUtilityTransaction> phoneUtilityTransactionHolder = new ThreadLocal<PhoneUtilityTransaction>();
	private volatile CountDownLatch writeAccessLatch;
	private static final int GLOBAL_SETTINGS = 129;
	private static final int GLOBAL_SETTINGS_ID = 130;

	private PhoneUtilitySQLiteOpenHelper phoneUtilitySQLiteOpenHelper;

	private static final UriMatcher uriMatcher = new UriMatcher(
			UriMatcher.NO_MATCH);
	static {
		uriMatcher
				.addURI(PhoneUtilityContract.AUTHORITY,
						PhoneUtilityContract.PhoneUtilityGlobalSettings.GLOBAL_SETTINGS,
						GLOBAL_SETTINGS);
		uriMatcher.addURI(PhoneUtilityContract.AUTHORITY,
				PhoneUtilityContract.PhoneUtilityGlobalSettings.GLOBAL_SETTINGS
						+ "/#", GLOBAL_SETTINGS_ID);
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		int result = 0;

		String tableName = null;
		int matchResult = uriMatcher.match(uri);
		switch (matchResult) {
		case GLOBAL_SETTINGS:
		case GLOBAL_SETTINGS_ID: {
			tableName = PhoneUtilitySQLiteOpenHelper.Tables.GLOBAL_SETTINGS;
			switch (matchResult) {
			case GLOBAL_SETTINGS:
				break;
			case GLOBAL_SETTINGS_ID:
				String globalSettingsId = uri.getLastPathSegment();

				selection = PhoneUtilityContract.PhoneUtilityGlobalSettings._ID
						+ "="
						+ globalSettingsId
						+ (TextUtils.isEmpty(selection) == false ? " AND ("
								+ selection + ')' : "");
				break;
			}

			break;
		}
		default:
			throw new IllegalArgumentException("Unknown URI: " + uri);
		}
		PhoneUtilityTransaction phoneUtilityTransaction = startTransaction(false);
		try {
			SQLiteDatabase db = phoneUtilityTransaction
					.getDbForTag(PHONE_UTILITY_CONTACT_TAG);
			result = db.delete(tableName, selection, selectionArgs);
			phoneUtilityTransaction.markSuccessful(false);
			return result;
		} finally {
			endTransaction(false);
		}
	}

	@Override
	public String getType(Uri uri) {
		return null;
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		String tableName = null;
		int matchResult = uriMatcher.match(uri);
		switch (matchResult) {
		case GLOBAL_SETTINGS: {
			tableName = PhoneUtilitySQLiteOpenHelper.Tables.GLOBAL_SETTINGS;
			break;
		}
		case GLOBAL_SETTINGS_ID: {
			throw new UnsupportedOperationException(
					"Insert with id does nost support!, matchResult: "
							+ matchResult);
		}
		default:
			throw new IllegalArgumentException("Unknown URI: " + uri);
		}

		PhoneUtilityTransaction phoneUtilityTransaction = startTransaction(false);
		try {
			SQLiteDatabase db = phoneUtilityTransaction
					.getDbForTag(PHONE_UTILITY_CONTACT_TAG);
			long rowId = db.insert(tableName, null, values);
			if (rowId > 0) {
				Uri result = ContentUris.withAppendedId(uri, rowId);
				phoneUtilityTransaction.markSuccessful(false);
				return result;
			}
		} finally {
			endTransaction(false);
		}
		throw new SQLException("Failed to insert row into " + uri);
	}

	@Override
	public boolean onCreate() {
		this.phoneUtilitySQLiteOpenHelper = new PhoneUtilitySQLiteOpenHelper(
				getContext());
		this.writeAccessLatch = new CountDownLatch(1);
		this.writeAccessLatch.countDown();
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
		String tableName = null;
		int matchResult = uriMatcher.match(uri);
		switch (matchResult) {
		case GLOBAL_SETTINGS:
		case GLOBAL_SETTINGS_ID: {
			tableName = PhoneUtilitySQLiteOpenHelper.Tables.GLOBAL_SETTINGS;
			switch (matchResult) {
			case GLOBAL_SETTINGS:
				break;
			case GLOBAL_SETTINGS_ID:
				queryBuilder
						.appendWhere(PhoneUtilityContract.PhoneUtilityGlobalSettings._ID
								+ "=" + uri.getLastPathSegment());
				break;
			}

			break;
		}
		default:
			throw new IllegalArgumentException("Unknown URI: " + uri);
		}
		queryBuilder.setTables(tableName);

		SQLiteDatabase database = this.phoneUtilitySQLiteOpenHelper
				.getReadableDatabase();
		Cursor cursor = queryBuilder.query(database, projection, selection,
				selectionArgs, null, null, sortOrder);
		cursor.setNotificationUri(getContext().getContentResolver(), uri);
		return cursor;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		String tableName = null;
		int matchResult = uriMatcher.match(uri);
		switch (matchResult) {
		case GLOBAL_SETTINGS:
		case GLOBAL_SETTINGS_ID: {
			tableName = PhoneUtilitySQLiteOpenHelper.Tables.GLOBAL_SETTINGS;
			switch (matchResult) {
			case GLOBAL_SETTINGS:
				break;
			case GLOBAL_SETTINGS_ID:
				String globalSettingsId = uri.getLastPathSegment();

				selection = PhoneUtilityContract.PhoneUtilityGlobalSettings._ID
						+ "="
						+ globalSettingsId
						+ (TextUtils.isEmpty(selection) == false ? " AND ("
								+ selection + ')' : "");
				break;
			}

			break;
		}
		default:
			throw new IllegalArgumentException("Unknown URI: " + uri);
		}
		int result = 0;
		PhoneUtilityTransaction phoneUtilityTransaction = startTransaction(false);
		try {
			SQLiteDatabase db = phoneUtilityTransaction
					.getDbForTag(PHONE_UTILITY_CONTACT_TAG);
			result = db.update(tableName, values, selection, selectionArgs);
			phoneUtilityTransaction.markSuccessful(false);
			return result;
		} finally {
			endTransaction(false);
		}
	}

	@Override
	public ContentProviderResult[] applyBatch(
			ArrayList<ContentProviderOperation> operations)
			throws OperationApplicationException {
		if (operations == null) {
			return null;
		}

		waitForAccess(writeAccessLatch);

		ContentProviderResult[] result = new ContentProviderResult[operations
				.size()];
		int i = 0;
		PhoneUtilityTransaction phontUtilityTransaction = startTransaction(true);
		try {
			for (ContentProviderOperation operation : operations) {
				result[i++] = operation.apply(this, result, i);
			}

			phontUtilityTransaction.markSuccessful(true);
			return result;
		} finally {
			endTransaction(true);
		}
	}

	private void waitForAccess(CountDownLatch latch) {
		if (latch == null) {
			return;
		}

		while (true) {
			try {
				latch.await();
				return;
			} catch (InterruptedException e) {
				Thread.currentThread().interrupt();
			}
		}
	}

	private PhoneUtilityTransaction startTransaction(boolean callerIsBatch) {
		PhoneUtilityTransaction transaction = this.phoneUtilityTransactionHolder
				.get();
		if (transaction == null) {
			transaction = new PhoneUtilityTransaction(callerIsBatch);

			if (this.phoneUtilitySQLiteOpenHelper != null) {
				transaction
						.startTransactionForDb(
								this.phoneUtilitySQLiteOpenHelper
										.getWritableDatabase(),
								PHONE_UTILITY_CONTACT_TAG, this);

			}
			this.phoneUtilityTransactionHolder.set(transaction);
		}
		return transaction;
	}

	private void endTransaction(boolean callerIsBatch) {
		PhoneUtilityTransaction transaction = this.phoneUtilityTransactionHolder
				.get();

		if (transaction != null && (!transaction.isBatch() || callerIsBatch)) {
			getContext().getContentResolver().notifyChange(
					PhoneUtilityContract.AUTHORITY_URI, null);
			transaction.finish(callerIsBatch);
			phoneUtilityTransactionHolder.set(null);
		}
	}

	@Override
	public void onBegin() {
	}

	@Override
	public void onCommit() {
	}

	@Override
	public void onRollback() {
	}

}
